Abraxus's Blog

HackPack Function Pointer Fun Write Up

Details:

Jeopardy style CTF

Category: Reverse Engineering

Comments:

Somehow, by means unbeknowst to us, you have access to Melon Eusk's terminal. But can you crack his password? Rumor is he wrote his own verification algorithm...

nc ctf2021.hackpack.club 10998

Write up:

I started by decompiling the function, the main function looked like:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; 
  bool changed; 
  int i;
  int (*fp)(void);
  char seed[5];
  unsigned __int64 v8;

  v8 = __readfsqword(0x28u);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  *(_DWORD *)seed = 0;
  seed[4] = 0;
  printf("Hello, Mr. Eusk. \nPassword > ");
  __isoc99_scanf("%4s", seed);
  changed = 0;
  for ( i = 0; i <= 3; ++i )
  {
    if ( seed[i] )
      changed = 1;
  }
  if ( !changed )
  {
    puts("You gotta give an input!");
    result = 1;
  }
  else
  {
    fp = pickFunction(seed);
    ((void (__fastcall *)(char *))fp)(seed);
    result = 0;
  }
  return result;
}

I noticed the pickFunction function and decided to decompile that as well:

int (*__cdecl pickFunction(char *seed))(void)
{
  char res; 

  res = (seed[2] | seed[3]) & (*seed | seed[1]);
  if ( res == 73 )
    return funTwo;
  if ( res > 0 && res <= 31 )
    return funOne;
  if ( res > 31 && res <= 63 )
    return funThree;
  if ( res <= 63 || res > 95 )
    return funFive;
  return funFour;
}

This function took in a char array contianing 4 chars and then used those to pick a function. I then needed to check which function had what I needed. I found out that funcTwo had what I needed:

int __cdecl funTwo()
{
  FILE *fp; 
  char flag[25]; 
  unsigned __int64 v3; 

  v3 = __readfsqword(0x28u);
  fp = fopen("flag", "r");
  fgets(flag, 25, fp);
  puts(flag);
  return 1;
}

I now needed to write a script to figure out what 4 characters I should send, I decided to use Z3:

# import z3
from z3 import *

# instantiate solver
s = Solver()

# create the 4 values
a = BitVec(f'a', 8)
b = BitVec(f'b', 8)
c = BitVec(f'c', 8)
d = BitVec(f'd', 8)

# add constraints
s.add(a < 127)
s.add(b < 127)
s.add(c < 127)
s.add(d < 127)
s.add(a > 32)
s.add(b > 32)
s.add(c > 32)
s.add(d > 32)
s.add(((c|d)&(a|b))==73)

# check the solve
print(s.check())
print(s.assertions())
m = s.model()

# print the model
print(m)

When run this output:

sat
[a < 127,
 b < 127,
 c < 127,
 d < 127,
 a > 32,
 b > 32,
 c > 32,
 d > 32,
 (c | d) & (a | b) == 73]
[b = 48, a = 105, c = 64, d = 73]

From this I was able to figure out the four characters to send were:

i0@I

I then connected to the server and tried my solve:

nc ctf2021.hackpack.club 10998

Hello, Mr. Eusk. 
Password > i0@I
flag{c1RcU1t5_R_fUn!2!}